-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.


local log = require 'mc.logging'
local initiator = require 'mc.initiator'
local ctx = require 'mc.context'
local skynet = require 'skynet'

local CHASSIS_INTF = 'bmc.kepler.Chassis'

local chassis_manager = {}
chassis_manager.__index = chassis_manager

function chassis_manager.new_for_csr(obj, position)
    obj['bmc.kepler.Chassis']:enable_shm_prop_cache('DeviceSpecication', false)
    return setmetatable({chassis_object = obj, device_count = {}, position = position, connector_num = {},
        connector_map = {}, device_map = {}}, chassis_manager)
end

function chassis_manager:get_intrusion_ac_on()
    return self.chassis_object.IntrusionACOn
end

function chassis_manager:get_intrusion_ac_off()
    return self.chassis_object.IntrusionACOff
end

function chassis_manager:get_intrusion_ac_on_clear()
    return self.chassis_object.IntrusionACOnClear
end

function chassis_manager:get_intrusion_ac_off_clear()
    return self.chassis_object.IntrusionACOffClear
end

function chassis_manager:set_intrusion_ac_on_clear(intrusion_clear_code)
    self.chassis_object.IntrusionACOnClear = intrusion_clear_code
end

function chassis_manager:set_intrusion_ac_off_clear(intrusion_clear_code)
    self.chassis_object.IntrusionACOffClear = intrusion_clear_code
end

function chassis_manager:get_cover_status()
    return self.chassis_object.CoverStatus
end

function chassis_manager:get_intrusion_flag()
    return self.chassis_object.IntrusionFlag
end

function chassis_manager:set_intrusion_flag(intrusion_flag_code)
    -- 防止信号变更过快，sleep让出协程
    skynet.sleep(1)
    self.chassis_object.IntrusionFlag = intrusion_flag_code
end

function chassis_manager:get_last_intrusion_ac_on()
    return self.chassis_object.LastIntrusionACOn
end

function chassis_manager:set_last_intrusion_ac_on(intrusion_ac_on_code)
    self.chassis_object.LastIntrusionACOn = intrusion_ac_on_code
end

function chassis_manager:set_prop(prop_name, value)
    self.chassis_object[prop_name] = value
end

function chassis_manager:get_prop(prop_name)
    return self.chassis_object[prop_name]
end

function chassis_manager:get_device_max_count(device_name)
    for _, device_info in pairs(self.chassis_object.DeviceSpecication) do
        if device_info[1] == device_name then
            return device_info[2]
        end
    end
end

function chassis_manager:set_device_max_count(device_name, max_num)
    if not self.chassis_object.DeviceSpecication then
        log:error("[chassis]set_device_max_count: set % max num failed", device_name)
        return
    end
    for _, device_info in pairs(self.chassis_object.DeviceSpecication) do
        if device_info[1] == device_name then
            device_info[2] = max_num
            return
        end
    end
    local device_info = {device_name, max_num}
    table.insert(self.chassis_object.DeviceSpecication, device_info)
end

function chassis_manager:get_device_num(device_name)
    return self.device_map[device_name] or 0
end

function chassis_manager:get_connector_num(connector_name)
    return self.connector_map[connector_name] or 0
end

function chassis_manager:update_device_num(device_name, device_num)
    self.device_map[device_name] = device_num
end

function chassis_manager:update_connector_num(connector_name, connector_num)
    self.connector_map[connector_name] = connector_num
end

function chassis_manager:init_connector()
    self.connector_num = {}
end

function chassis_manager:update_connector_max_count(device_name)
    local max_num = self.connector_num[device_name] or 0
    max_num = max_num + 1
    self.connector_num[device_name] = max_num
end

function chassis_manager:update_connector()
    for connector_name, connector_num in pairs(self.connector_num) do
        local device_num = self:get_device_num(connector_name)
        self:update_connector_num(connector_name, connector_num)
        local real_max_num = (device_num > connector_num) and device_num or connector_num
        log:debug('[chassis]update connectors, device_name:%s, connector_num:%s, device_num:%s, real_num:%s',
            connector_name, connector_num, device_num, real_max_num)
        self:set_device_max_count(connector_name, real_max_num)
    end
end

function chassis_manager:update_device(device_name, device_num)
    local connector_num = self:get_connector_num(device_name)
    self:update_device_num(device_name, device_num)
    local real_max_num = (connector_num > device_num) and connector_num or device_num
    log:debug('[chassis]update devices, device_name:%s, connector_num:%s, device_num:%s, real_num:%s',
        device_name, connector_num, device_num, real_max_num)
    self:set_device_max_count(device_name, real_max_num)
end

function chassis_manager:property_change()
    self.chassis_object[CHASSIS_INTF].property_before_change:on(function(name, value, sender)
        if sender == nil then
            return true
        end
        if name ~= 'Location' then
            return true
        end

        local m_initiator = initiator.new('N/A', 'N/A', 'localhost')
        local context = ctx.get_context()
        local _initiator = context and context:get_initiator() or m_initiator
        log:operation(_initiator, 'Chassis', 'Set chassis location to %s successfully', value)
        return true
    end)
end

return chassis_manager